import numpy as np
from numpy.fft import fftfreq,fftshift
import matplotlib.pyplot as plt
import sys
sys.path.append("..") # for pfb tools imports etc
from pfb import forward_pfb,inverse_pfb,quantize_8_bit,quantize_8_bit_real,quantize_8_bit_spec_scaled_per_channel
import conjugate_gradient as cg
# shortcut variables
sqrt=np.sqrt
LFRAME=2048 # Length of a U1 frame
NCHAN=LFRAME//2+1 # Number of channels
NTAP=4 # Number of taps
NFRAMES=16000 # Number of frames of data to inspect
NFRAMES_OFFSET=100 # Number of frames to skip at the beginning
SR=10e6 # 10MHz Sample Rate
CF=35e6 # 35MHz Central Frequency
FNAME="gqrx_20230601_175822_35000000_10000000_fc.raw"
# File name/Relative path to data file ^
ADC_DELTA=0.04 # ADC quantization interval
COMPLEX_DELTA=0.25 # Quantization delta of channelized signal
IMSCALE_PLT=(-2.5,1)# Set scale of spectrogram plot colors
IMSCALE_RES=(-2.5,-1)# Set scale of residual spectrogram colors
CMAP_RES="terrain" # colormap of residual plts, default "gist_rainbow"
Waterfall plotting tool
from mpl_toolkits.axes_grid1 import make_axes_locatable
def waterfall(spec,sr,cf,title=None,imshow_scale=IMSCALE_PLT,
cmap="gist_rainbow"):
"""Waterfall plot.
Parameters
----------
spec : 2darray
The spectrum to plot
sr : float
Sample Rate (ADC)
cf : float
Central Frequency
title : str
The title of the plot.
imshow_scale : tuple
Two tuple of floats. The first one sets the min
value to display, the second sets max value.
cmap : str
Name of the pyplot colormap.
"""
if title is None:
title=f"Central Freq {cf/1e6:.0f}MHz, Sample Rate {sr/1e6:.0f}MHz"
nframes,nchan=spec.shape
lframe=2*(nchan-1)
fig,ax=plt.subplots(figsize=(6,6))
log_spec = np.log(abs(spec)+1e-16) # add some noise for plotting purposes
# Set the color scale by "modifying" spectrum (purely for visuals)
log_spec[0,0]=imshow_scale[1] # Hack, set first pixel to cieling
log_spec[0,1]=imshow_scale[0] # Hack, set second pixel to floor
if (log_spec>imshow_scale[1]).any():
print("WARNiNG: Spectrum has higher ceiling than is displayed!")
log_spec[np.where(log_spec>imshow_scale[1])] = imshow_scale[1]
log_spec[np.where(log_spec<imshow_scale[0])] = imshow_scale[0]
freqs=fftshift(fftfreq(lframe,d=1/sr))[::2] + cf
im = ax.imshow(log_spec,cmap=cmap)
plt.title(title,fontsize=18)
ax.set_xlabel("Frequency MHz")
# plt.xticks(freqs)
xticks=np.arange(0,len(freqs),len(freqs)//6)
ax.set_xticks(xticks)
ax.set_xticklabels(["{:.1f}".format(i) for i in freqs[xticks]/1e6])
ax.set_ylabel("Time (s)")
yticks=np.arange(0,nframes,nframes//6)
# colorbar stuff
divider=make_axes_locatable(ax)
cax = divider.append_axes('right',size='5%',pad=0.05)
fig.colorbar(im,cax,orientation='vertical')
ax.set_yticks(yticks)
ax.set_yticklabels(["{:.2f}".format(i) for i in yticks * lframe/sr])
plt.tight_layout()
plt.show()
# Load from raw file
def load_sig(path:str, verbose=False):
"""Load the signal from binary file located at path"""
sig=np.fromfile(path,dtype="float32") # load signal from file
sig=(sig-sig.mean())/sig.std() # normalize
sig=sig[LFRAME*NFRAMES_OFFSET:(NFRAMES+NFRAMES_OFFSET)*LFRAME] # trim signal
sigadc=quantize_8_bit_real(sig,delta=ADC_DELTA) # ADC quantize signal
if verbose is True:
plt.figure()
plt.plot(sig[:512],label="RAW")
plt.plot(sigadc[:512],linewidth=1.5,label="ADC")
plt.legend()
plt.tight_layout()
plt.show()
return sigadc
sig=load_sig(FNAME)
# nframe_full is global!
nframe_full=len(sig)//LFRAME # number of frames in full signal
sig=sig[:(len(sig)//LFRAME)*LFRAME] # trunkate signal
# Get the full channelized signal (for later)
spec_adc_full=forward_pfb(sig,NCHAN,NTAP)
# Quantize the spectrum, 4+4bits=1byte (real+imag)
spec_adc_full_q,std_per_chan=quantize_8_bit_spec_scaled_per_channel(
spec_adc_full, COMPLEX_DELTA)
del spec_adc_full # don't need this, takes up lots of memory
# Display channelized signal, U=1
u=1
# Get spectrum and plot it
spec_adc_u1=forward_pfb(sig[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_adc_u1/sqrt(u*LFRAME), # normalize spectrum
SR,CF,title=f"Channelized ADC'd E-field U={u}")
# Get 4+4 bit quantized spectrum and plot residuals
spec_adc_u1_q,_=quantize_8_bit_spec_scaled_per_channel(
spec_adc_u1, COMPLEX_DELTA)
waterfall((spec_adc_u1_q-spec_adc_u1)/spec_adc_u1.std(axis=0), SR, CF,
title=f"Channelized ADC'd E-field U={u}\
\n4+4 bit-quantized, Residuals\
\n(normalized by power in each channel)",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=2
u=2
# Get spectrum, plot it
spec_adc_u2=forward_pfb(sig[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_adc_u2/sqrt(u*LFRAME),
SR, CF,title=f"Channelized ADC'd E-field U={u}")
# Quantize spectrum, plot residuals
spec_adc_u2_q,_=quantize_8_bit_spec_scaled_per_channel(
spec_adc_u2, COMPLEX_DELTA)
waterfall((spec_adc_u2_q-spec_adc_u2)/spec_adc_u2.std(axis=0),
SR, CF, title=f"Channelized ADC'd E-field U={u}\
\n4+4 bit quantized, Residuals\
\n(normalized by power in each channel)",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=4
u=4
# Get spectrum, plot it
spec_adc_u4=forward_pfb(sig[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_adc_u4/sqrt(u*LFRAME),
SR, CF,title=f"Channelized ADC'd E-field U={u}")
# Quantize spectrum, plot diff (residuals)
spec_adc_u4_q,_=quantize_8_bit_spec_scaled_per_channel(
spec_adc_u4, COMPLEX_DELTA)
waterfall((spec_adc_u4_q-spec_adc_u4)/spec_adc_u4.std(axis=0),
SR, CF, title=f"Channelized ADC'd E-field U={u}\
\n4+4 bit quantized, residuals\
\n(normalized by power in each channel)",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
sig_ipfb=inverse_pfb(spec_adc_full_q) # Apply inverse PFB
sig_ipfb,imag = sig_ipfb.real,sig_ipfb.imag # Drop the imaginary part
print(f"Sanity check: {sig_ipfb.std()} >> {imag.std()}")
del imag # delete this variable to clear memory
Sanity check: 1.0194471261087377 >> 1.8295780703350323e-16
# Display channelized signal, U=1
u=1
spec_ipfb_u1=forward_pfb(sig_ipfb[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_ipfb_u1/sqrt(u*LFRAME),
SR, CF,title=f"Channelized IPFB'd signal U={u}")
waterfall((spec_ipfb_u1-spec_adc_u1_q)/spec_adc_u1_q.std(axis=0),
SR, CF,title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display re-channelized signal, U=2
u=2
spec_ipfb_u2=forward_pfb(sig_ipfb[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_ipfb_u2/sqrt(u*LFRAME),
SR, CF, title=f"Re-Channelized IPFB'd signal U={u}")
waterfall((spec_ipfb_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF, title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display re-channelized signal, U=4
u=4
spec_ipfb_u4=forward_pfb(sig_ipfb[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_ipfb_u4/sqrt(u*LFRAME),
SR, CF, title=f"Re-Channelized IPFB'd signal U={u}")
waterfall((spec_ipfb_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF, title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
sig_wien=inverse_pfb(spec_adc_full_q, wiener_thresh=0.1) # Apply inverse PFB
sig_wien,imag = sig_wien.real,sig_wien.imag # Drop the imaginary part
print(f"Sanity check: {sig_wien.std()} >> {imag.std()}")
del imag # delete this variable to clear memory
Sanity check: 0.9906092816117328 >> 1.7893473424362953e-16
# Display channelized signal, U=1
u=1
spec_wien_u1=forward_pfb(sig_wien[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_wien_u1/sqrt(u*LFRAME),
SR, CF,title=f"Channelized IPFB'd\nWiener-filtred signal U={u}")
waterfall((spec_wien_u1-spec_adc_u1_q)/spec_adc_u1_q.std(axis=0),
SR, CF,title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display re-channelized signal, U=2
u=2
spec_wien_u2=forward_pfb(sig_wien[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_wien_u2/sqrt(u*LFRAME),
SR, CF,
title=f"Re-Channelized IPFB'd\nWiener-filtered signal U={u}")
waterfall((spec_wien_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF, title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display re-channelized signal, U=2
u=4
spec_wien_u4=forward_pfb(sig_wien[:1024*u*u*LFRAME],(NCHAN-1)*u+1,NTAP)
waterfall(spec_wien_u4/sqrt(u*LFRAME),
SR, CF,
title=f"Re-Channelized IPFB'd\nWiener-filtered signal U={u}")
waterfall((spec_wien_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF, title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Construct B operator and u vector without a prior
B,u = cg.get_Bu_noprior(spec_adc_full_q,
delta=COMPLEX_DELTA,
ntap=NTAP)
# CG descent onto reconstructed timestream without prior
sig_cg_noprior=cg.conjugate_gradient_descent(B,u,x0=sig_wien,
x_true=sig,
max_iter=5,
verbose=True,
k=nframe_full,
rmin=0)
INFO: Conjugate Gradient descent completed.
# Load 5% indices that make up your prior
# (with 5%, best result npersave=2, max_iter=1)
_,saved_idxs_5perc=cg.get_saved_idxs(n_per_save=2, prct=0.05,
k=nframe_full, lblock=LFRAME)
# Construct B operator and u vector using prior
B,u,chisq = cg.get_Bu_withprior(sig, spec_adc_full_q,
delta=COMPLEX_DELTA,
saved_idxs=saved_idxs_5perc,
ntap=NTAP)
# CG descend onto reconstructed timestream `ipfb_cg`
sig_cg_5perc=cg.conjugate_gradient_descent(B,u,x0=sig_wien,
x_true=sig,
max_iter=1,
verbose=True,
k=nframe_full,
rmin=0)
INFO: Conjugate Gradient descent completed.
# Display channelized signal, U=1
u=1
spec_cg_5perc_u1=forward_pfb(sig_cg_5perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_5perc_u1/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 5% U={u}")
waterfall((spec_cg_5perc_u1-spec_adc_u1_q)/spec_adc_u1_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=2
u=2
spec_cg_5perc_u2=forward_pfb(sig_cg_5perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_5perc_u2/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 5% U={u}")
waterfall((spec_cg_5perc_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=4
u=4
spec_cg_5perc_u4=forward_pfb(sig_cg_5perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_5perc_u4/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 5% U={u}")
waterfall((spec_cg_5perc_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Load 5% indices that make up your prior
# (with 5%, best result npersave=1, max_iter=1 (confusingly means 1step))
_,saved_idxs_10perc=cg.get_saved_idxs(n_per_save=1, prct=0.10,
k=nframe_full, lblock=LFRAME)
# Construct B operator and u vector using prior
B,u,chisq = cg.get_Bu_withprior(sig, spec_adc_full_q,
delta=COMPLEX_DELTA,
saved_idxs=saved_idxs_10perc,
ntap=NTAP)
# CG descend onto reconstructed timestream `ipfb_cg`
sig_cg_10perc=cg.conjugate_gradient_descent(B,u,x0=sig_wien,
x_true=sig,
max_iter=1,
verbose=True,
k=nframe_full,
rmin=0)
INFO: Conjugate Gradient descent completed.
# Display channelized signal, U=1
u=1
spec_cg_10perc_u1=forward_pfb(sig_cg_10perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_10perc_u1/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 10% U={u}")
waterfall((spec_cg_10perc_u1-spec_adc_u1_q)/spec_adc_u1_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=2
u=2
spec_cg_10perc_u2=forward_pfb(sig_cg_10perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_10perc_u2/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 10% U={u}")
waterfall((spec_cg_10perc_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=4
u=4
spec_cg_10perc_u4=forward_pfb(sig_cg_10perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_10perc_u4/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 10% U={u}")
waterfall((spec_cg_10perc_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Load 5% indices that make up your prior
# (with 5%, best result npersave=2, max_iter=2 (confusingly means 1step))
_,saved_idxs_20perc=cg.get_saved_idxs(n_per_save=1, prct=0.20,
k=nframe_full, lblock=LFRAME)
# Construct B operator and u vector using prior
B,u,chisq = cg.get_Bu_withprior(sig, spec_adc_full_q,
delta=COMPLEX_DELTA,
saved_idxs=saved_idxs_20perc,
ntap=NTAP)
# CG descend onto reconstructed timestream `ipfb_cg`
sig_cg_20perc=cg.conjugate_gradient_descent(B,u,x0=sig_wien,
x_true=sig,
max_iter=1,
verbose=True,
k=nframe_full,
rmin=0)
INFO: Conjugate Gradient descent completed.
# Display channelized signal, U=1
u=1
spec_cg_20perc_u1=forward_pfb(sig_cg_20perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_20perc_u1/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 20% U={u}")
waterfall((spec_cg_20perc_u1-spec_adc_u1_q)/spec_adc_u1_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=2
u=2
spec_cg_20perc_u2=forward_pfb(sig_cg_20perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_20perc_u2/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 20% U={u}")
waterfall((spec_cg_20perc_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# Display channelized signal, U=2
u=4
spec_cg_20perc_u4=forward_pfb(sig_cg_20perc[:1024*u*u*LFRAME],
(NCHAN-1)*u+1, NTAP)
waterfall(spec_cg_20perc_u4/sqrt(u*LFRAME),
SR, CF,
title=f"Channelized CG 20% U={u}")
waterfall((spec_cg_20perc_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF,
title=f"Residuals, normalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# IPFB
waterfall((spec_ipfb_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF, title=f"Residuals, IPFB'd naively\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# Wiener filter
waterfall((spec_wien_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF, title=f"Residuals, Wiener Filtered\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# 5% extra timestream data saved
waterfall((spec_cg_5perc_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF,
title=f"Residuals, CG 5% data saved\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# 10% extra timestream data saved
waterfall((spec_cg_10perc_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF,
title=f"Residuals, CG 10% data saved\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# 20% extra timestream data saved
waterfall((spec_cg_20perc_u2-spec_adc_u2_q)/spec_adc_u2_q.std(axis=0),
SR, CF,
title=f"Residuals, CG 20% data saved\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
# IPFB
waterfall((spec_ipfb_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF, title=f"Residuals, IPFB'd naively\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# Wiener filter
waterfall((spec_wien_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF, title=f"Residuals, Wiener Filtered\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# 5% extra timestream data saved
waterfall((spec_cg_5perc_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF,
title=f"Residuals, CG 5% data saved\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# 10% extra timestream data saved
waterfall((spec_cg_10perc_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF,
title=f"Residuals, CG 10% data saved\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
# 20% extra timestream data saved
waterfall((spec_cg_20perc_u4-spec_adc_u4_q)/spec_adc_u4_q.std(axis=0),
SR, CF,
title=f"Residuals, CG 20% data saved\
\nnormalized by channel",
imshow_scale=IMSCALE_RES,cmap=CMAP_RES)
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!
WARNiNG: Spectrum has higher ceiling than is displayed!